Skip to content

Fix ML-DSA MakePublicKey derivation, verify guards, and ASN derivation testing - #10985

Open
stenslae wants to merge 4 commits into
wolfSSL:masterfrom
stenslae:fix-mldsa-privkey-derive-pubkey
Open

Fix ML-DSA MakePublicKey derivation, verify guards, and ASN derivation testing#10985
stenslae wants to merge 4 commits into
wolfSSL:masterfrom
stenslae:fix-mldsa-privkey-derive-pubkey

Conversation

@stenslae

@stenslae stenslae commented Jul 23, 2026

Copy link
Copy Markdown
Member

Description

Refactored ML-DSA public key derivation to fix memory allocation issues in SMALL_MEM builds, and adds guards to ensure verification isn't attempted without a valid public key. Introduced ECC best-effort public key derivation when decoding private-only SEC1 keys. Added additional CI testing.

Notes:

  • ML-DSA verify now returns PUBLIC_KEY_E instead of BAD_FUNC_ARG when no
    public key is set.
  • ECC private key decode now derives the public point by default. Define WOLFSSL_NO_ECC_DERIVE_PUB_ON_DECODE to opt out.
  • ecc_make_pub_ex() no longer marks a key ECC_PRIVATEKEY when derivation failed.

wolfSSL/wolfssh#1120

Testing

Added test coverage.

Checklist

  • added tests
  • updated/added doxygen
  • updated appropriate READMEs
  • Updated manual and documentation

@stenslae stenslae self-assigned this Jul 23, 2026
@stenslae
stenslae marked this pull request as ready for review July 23, 2026 18:06
@github-actions

Copy link
Copy Markdown

retest this please

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

gcc-arm-cortex-m0plus

  • FLASH: .text +76 B (+0.1%, 64,331 B / 262,144 B, total: 25% used)

gcc-arm-cortex-m3

  • FLASH: .text +68 B (+0.1%, 123,011 B / 262,144 B, total: 47% used)

gcc-arm-cortex-m4

  • FLASH: .text +64 B (+0.0%, 201,173 B / 262,144 B, total: 77% used)

gcc-arm-cortex-m4-baremetal

  • FLASH: .text +64 B (+0.1%, 66,955 B / 262,144 B, total: 26% used)

gcc-arm-cortex-m4-dtls13

  • FLASH: .text +64 B (+0.0%, 181,988 B / 1,048,576 B, total: 17% used)

gcc-arm-cortex-m4-min-ecc

  • FLASH: .text +64 B (+0.1%, 61,869 B / 262,144 B, total: 24% used)

gcc-arm-cortex-m4-openssl-compat

  • FLASH: .text +64 B (+0.0%, 772,908 B / 1,048,576 B, total: 74% used)

gcc-arm-cortex-m4-pkcs7

  • FLASH: .text +64 B (+0.0%, 213,972 B / 262,144 B, total: 82% used)

gcc-arm-cortex-m4-pq

  • FLASH: .rodata -32 B, .text +832 B (+0.3%, 281,888 B / 1,048,576 B, total: 27% used)

gcc-arm-cortex-m4-sp-math

  • FLASH: .text +64 B (+0.1%, 61,869 B / 262,144 B, total: 24% used)

gcc-arm-cortex-m4-tls12

  • FLASH: .text +64 B (+0.1%, 123,803 B / 262,144 B, total: 47% used)

gcc-arm-cortex-m4-tls13

  • FLASH: .text +64 B (+0.0%, 237,319 B / 262,144 B, total: 91% used)

gcc-arm-cortex-m7

  • FLASH: .text +64 B (+0.0%, 201,173 B / 262,144 B, total: 77% used)

gcc-arm-cortex-m7-pq

  • FLASH: .rodata -32 B, .text +832 B (+0.3%, 282,464 B / 1,048,576 B, total: 27% used)

gcc-arm-cortex-m7-tls13

  • FLASH: .text +64 B (+0.0%, 237,383 B / 262,144 B, total: 91% used)

linuxkm-pie

  • Data: __patchable_function_entries +32 B (+0.1%, 26,472 B)

linuxkm-standard

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #10985

Scan targets checked: wolfcrypt-bugs, wolfcrypt-rs-bugs, wolfcrypt-src, wolfssl-bugs, wolfssl-src

Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment thread wolfcrypt/src/wc_mldsa.c Outdated

@Frauschi Frauschi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐺 Skoll Code Review

Overall recommendation: REQUEST_CHANGES
Findings: 6 total — 4 posted, 2 skipped

Posted findings

  • [High] wc_MlDsaKey_MakePublicKey mishandles buffers in the small-mem path (aSet, h, s1)wolfcrypt/src/wc_mldsa.c:10413-10665
  • [Medium] wc_EccPrivateKeyDecode now unconditionally derives the public pointwolfcrypt/src/asn.c:32722-32745
  • [Low] Dead/empty guard branch before t0Scratch allocationwolfcrypt/src/wc_mldsa.c:10458-10467
  • [Medium] Duplicated t = A o NTT(s1) + s2 kernel - extract shared helper with keygenwolfcrypt/src/wc_mldsa.c:10479-10635
Skipped findings
  • [Medium] No test coverage for small-mem / cache-macro configurations of MakePublicKey
  • [Medium] ML-DSA secret vectors left unzeroed in wc_MlDsaKey_MakePublicKey under SMALL_MEM + CACHE_PRIV_VECTORS

Review generated by Skoll via Claude/Codex

Comment thread wolfcrypt/src/wc_mldsa.c
Comment thread wolfcrypt/src/asn.c Outdated
Comment thread wolfcrypt/src/wc_mldsa.c Outdated
Comment thread wolfcrypt/src/wc_mldsa.c
@stenslae
stenslae force-pushed the fix-mldsa-privkey-derive-pubkey branch from b6324ad to b72a8ed Compare July 27, 2026 17:05
Comment thread wolfcrypt/src/wc_mldsa.c Outdated
@stenslae
stenslae force-pushed the fix-mldsa-privkey-derive-pubkey branch 2 times, most recently from 106f551 to fa80057 Compare July 27, 2026 17:32

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #10985

Scan targets checked: wolfcrypt-bugs, wolfcrypt-rs-bugs, wolfcrypt-src, wolfssl-bugs, wolfssl-src

Findings: 3
3 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment thread wolfcrypt/src/wc_mldsa.c
Comment thread wolfcrypt/src/asn.c Outdated
Comment thread wolfcrypt/src/asn_orig.c Outdated
Comment thread wolfcrypt/src/asn_orig.c Outdated
Comment thread wolfcrypt/src/asn.c Outdated
Comment thread wolfcrypt/src/wc_mldsa.c
@stenslae
stenslae force-pushed the fix-mldsa-privkey-derive-pubkey branch from fa80057 to e292a3c Compare July 27, 2026 17:50

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #10985

Scan targets checked: wolfcrypt-bugs, wolfcrypt-rs-bugs, wolfcrypt-src, wolfssl-bugs, wolfssl-src

Findings: 3
3 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment thread wolfcrypt/src/wc_mldsa.c Outdated
Comment thread wolfcrypt/src/wc_mldsa.c Outdated
Comment thread tests/api/test_mldsa.c
Comment thread tests/api/test_mldsa.c
Comment thread wolfcrypt/src/wc_mldsa.c Outdated
Comment thread wolfcrypt/src/wc_mldsa.c Outdated
@stenslae
stenslae force-pushed the fix-mldsa-privkey-derive-pubkey branch from e292a3c to ceb0a7d Compare July 27, 2026 20:48

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #10985

Scan targets checked: wolfcrypt-bugs, wolfcrypt-rs-bugs, wolfcrypt-src, wolfssl-bugs, wolfssl-src

Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment thread wolfcrypt/src/wc_mldsa.c
Comment thread wolfcrypt/src/wc_mldsa.c
@stenslae
stenslae force-pushed the fix-mldsa-privkey-derive-pubkey branch from ceb0a7d to 3d9668e Compare July 27, 2026 21:54
@stenslae
stenslae force-pushed the fix-mldsa-privkey-derive-pubkey branch from 3d9668e to 2527a37 Compare July 27, 2026 22:11

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #10985

Scan targets checked: wolfcrypt-bugs, wolfcrypt-rs-bugs, wolfcrypt-src, wolfssl-bugs, wolfssl-src

Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment thread wolfcrypt/src/wc_mldsa.c Outdated

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #10985

Scan targets checked: wolfcrypt-bugs, wolfcrypt-rs-bugs, wolfcrypt-src, wolfssl-bugs, wolfssl-src

Findings: 2
2 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment thread wolfcrypt/src/wc_mldsa.c Outdated
Comment thread wolfcrypt/src/asn_orig.c Outdated
Comment thread wolfcrypt/src/wc_mldsa.c Outdated
Comment thread wolfcrypt/src/asn_orig.c Outdated
@stenslae
stenslae force-pushed the fix-mldsa-privkey-derive-pubkey branch 2 times, most recently from 9e69c56 to afe7a9f Compare August 3, 2026 20:56

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #10985

Scan targets checked: wolfcrypt-bugs, wolfcrypt-rs-bugs, wolfcrypt-src, wolfssl-bugs, wolfssl-src

Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment thread wolfcrypt/src/asn.c Outdated
Comment thread wolfcrypt/src/asn.c Outdated
@stenslae stenslae removed their assignment Aug 3, 2026
@dgarske
dgarske requested a review from Frauschi August 3, 2026 23:17

@Frauschi Frauschi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐺 Skoll Code Review

Overall recommendation: REQUEST_CHANGES
Findings: 23 total — 20 posted, 3 skipped

Posted findings

  • [High] New ECC derive test's #if is missing four terms, so it fails in the opt-out, no-key-export and crypto-cb-only buildstests/api/test_asn.c:2854-2860
  • [High] test_wc_MldsaDecisionCoverage2 PUBLIC_KEY_E expectation is not gated on the conditions that enable the KeyToDer auto-derive; fails with --enable-mldsa=sign,verifytests/api/test_mldsa.c:31734-31749
  • [High] t0Scratch allocated without the 8-byte AVX2 tail slack, giving a 3-byte heap overflow on every private-only public-key derivationwolfcrypt/src/wc_mldsa.c:10694-10749
  • [High] Fault-harden verify buffers packed back-to-back: 6-byte heap overflow and an always-failing self-check under AVX2wolfcrypt/src/wc_mldsa.c:10777-10813
  • [Medium] wc_MlDsaKey_KeyToDer lost its BAD_FUNC_ARG default: an unrecognised key->level now returns 0wolfcrypt/src/wc_mldsa.c:13436-13488
  • [Medium] Best-effort ECC public-key derivation is a no-op in every default (hardened) buildwolfcrypt/src/asn.c:33143-33150
  • [Medium] Unconditional extra base-point scalar multiply on every ECC private-key decode, including paths that discard the key immediatelywolfcrypt/src/asn.c:33290-33294
  • [Medium] EccDerivePubBestEffort is duplicated verbatim into asn_orig.c although asn_orig.c is #included into asn.c (same translation unit)wolfcrypt/src/asn_orig.c:7598-7654
  • [Medium] Async ECC derive: retry loop keys off the wrong enable macro, and ecc_make_pub_ex no longer upgrades key->type on WC_PENDING_Ewolfcrypt/src/asn.c:33152-33166
  • [Medium] ECC OOM sub-test derives its baseline allocation count from a different DER blob, so the whole block can silently no-optests/api/test_asn.c:2957-3001
  • [Medium] WC_MLDSA_FAULT_HARDEN silently overrides WOLFSSL_MLDSA_NO_CHECK_KEY, and wc_mldsa.h re-derives the condition instead of consuming the new macrowolfcrypt/src/wc_mldsa.c:254-263
  • [Medium] Widened CheckKey guard newly exposes a NULL key->a dereference to configurations where CheckKey previously did not existwolfcrypt/src/wc_mldsa.c:12036-12066
  • [Medium] Coverage gaps: PublicKeyToDer auto-derive, ML-DSA 65/87 OOM, cached-vector invalidation, MakePublicKey small-mem and fault-harden pathstests/api/test_mldsa.c:7761-8290
  • [Medium] Under WOLF_CRYPTO_CB_FIND the devId gate is ineffective, so every SEC1 private-key decode fires an unsolicited EC_MAKE_PUB crypto callbackwolfcrypt/src/asn.c:33136-33141
  • [Low] Documentation gaps for the new decode-time derivation: undocumented macro, inaccurate guard comment, un-mirrored Japanese doxygenwolfcrypt/src/asn.c:33118
  • [Low] MakePublicKey invalidates the private-vector cache in MAKE_KEY_SMALL_MEM builds where it never touched itwolfcrypt/src/wc_mldsa.c:10715-10720
  • [Low] Unguarded struct pokes dereference key after a failed allocation or a skipped Inittests/api/test_mldsa.c:7787-7789
  • [Low] test_mldsa_make_public_key_tr_mismatch comment claims to exercise the WC_MLDSA_FAULT_HARDEN recheck, which that path can never reachtests/api/test_mldsa.c:8248-8253
  • [Low] ECC private-key decode now leaves an unblinded plaintext copy of the private scalar in key->ku (WOLFSSL_ECC_BLIND_K builds)wolfcrypt/src/asn.c:33153-33165
  • [Info] *New PUBLIC_KEY_E early return in mldsa_verify_with_mu leaves res untouched, unlike every other error path in the functionwolfcrypt/src/wc_mldsa.c:9837-9839
Skipped findings
  • [Low] Coding-standard violations: comment punctuation, over-80-column lines and new bare scope blocks
  • [Low] WC_RNG is not zeroed before use, so the unconditional wc_FreeRng at the end operates on an uninitialised stack struct when wc_InitRng fails
  • [Info] Fault-harden branch documents a false invariant about matrix A after a failed CheckKey, and its aSet assignment is dead code

Review generated by Skoll via Claude/Codex

Comment thread tests/api/test_asn.c
Comment thread tests/api/test_mldsa.c
Comment thread wolfcrypt/src/wc_mldsa.c
Comment thread wolfcrypt/src/wc_mldsa.c
Comment thread wolfcrypt/src/wc_mldsa.c
Comment thread wolfcrypt/src/wc_mldsa.c Outdated
Comment thread tests/api/test_mldsa.c Outdated
Comment thread tests/api/test_mldsa.c Outdated
Comment thread wolfcrypt/src/asn.c Outdated
Comment thread wolfcrypt/src/wc_mldsa.c
@Frauschi Frauschi assigned stenslae and unassigned wolfSSL-Bot Aug 4, 2026
@stenslae
stenslae force-pushed the fix-mldsa-privkey-derive-pubkey branch from afe7a9f to e5044af Compare August 5, 2026 22:37

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #10985

Scan targets checked: wolfcrypt-bugs, wolfcrypt-rs-bugs, wolfcrypt-src, wolfssl-bugs, wolfssl-src

No new issues found in the changed files. ✅

@Frauschi

Frauschi commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Jenkins retest this please

@Frauschi Frauschi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐺 Skoll Code Review

Overall recommendation: REQUEST_CHANGES
Findings: 24 total — 15 posted, 9 skipped

Posted findings

  • [High] ecc_make_pub_ex no longer upgrades key->type on WC_PENDING_E, so async ECC verify with a private-only key never resumeswolfcrypt/src/ecc.c:5765-5769
  • [Medium] EccDerivePubBestEffort retries with WC_ASYNC_FLAG_CALL_AGAIN on an operation documented as not re-callablewolfcrypt/src/asn.c:33222-33234
  • [Medium] test_wc_EccPrivateKeyDecode_derive_pub guard omits !defined(WC_NO_RNG) and asserts behaviour the code deliberately skips theretests/api/test_asn.c:2785-2795,2856-2864
  • [Medium] tests/suites.c guards add ML-DSA sign but not ML-DSA verifytests/suites.c:1329-1333,1362-1366
  • [Medium] Public-key derivation now runs on every successful wc_EccPrivateKeyDecode, including probe/measure call sites that discard the keywolfcrypt/src/asn.c:33370-33374
  • [Medium] Best-effort ECC public-key derivation is abandoned when the temporary RNG cannot be created, and instantiates an unused DRBG on every decode in SP-math buildswolfcrypt/src/asn.c:33196-33220, wolfcrypt/src/asn_orig.c:7630-7654
  • [Low] EccDerivePubBestEffort duplicated verbatim (90 lines) in asn.c and asn_orig.c with no sync mechanismwolfcrypt/src/asn.c:33164-33253
  • [Low] New user-settable macro WOLFSSL_NO_ECC_DERIVE_PUB_ON_DECODE not registered in .wolfssl_known_macro_extraswolfcrypt/src/asn.c:33167-33173
  • [Low] *Public verify entry points return PUBLIC_KEY_E without zeroing res, unlike the internal guard they duplicatewolfcrypt/src/wc_mldsa.c:12175,12231,12293,12335
  • [Low] ECC OOM loop never asserts the injected failure actually firedtests/api/test_asn.c:2978-3010
  • [Low] ECC OOM guard omits WOLFSSL_MEM_FAIL_COUNT / WOLFSSL_FORCE_MALLOC_FAIL_TEST exclusions the ML-DSA harness hastests/api/test_asn.c:2785-2795,2943-2944
  • [Low] ForceZero runs over uninitialized stack scratch on every early-error return of wc_MlDsaKey_MakePublicKeywolfcrypt/src/wc_mldsa.c:11757-11765
  • [Low] Doxygen for the new/changed ML-DSA APIs is inconsistent with the implemented return codesdoc/dox_comments/header_files/wc_mldsa.h:260-282,853-855
  • [Low] wc_MlDsaKey_PublicKeyToDer() performs a full public-key derivation for the documented output==NULL size querywolfcrypt/src/wc_mldsa.c:14343-14360
  • [Info] WOLFSSL_ECC_BLIND_K scrub of key->ku added in the ASN layer instead of ecc_make_pub_ex(), leaving every other make-pub caller unscrubbedwolfcrypt/src/asn.c:33247-33249, wolfcrypt/src/asn_orig.c:7681-7683
Skipped findings
  • [Medium] WC_MLDSA_HAVE_CHECK_KEY's second arm is logically dead, and its condition is now hand-duplicated in three places that can drift
  • [Low] devId skip in EccDerivePubBestEffort is ineffective under WOLF_CRYPTO_CB_FIND
  • [Low] WOLFSSL_ECC_BLIND_K has no CI coverage, so the kua fix and the new key->ku scrub are never compiled
  • [Low] test_mldsa_make_public_key_cache_invalidation's core assertions never run in any CI job that executes unit.test
  • [Low] Roughly 30 added lines exceed the 80-column convention, including a 98-char comment duplicated in two files
  • [Low] wc_MlDsaKey_MakePublicKey has no wc_dilithium_* legacy alias
  • [Low] wc_mldsa.c gains an unconditional dependency on ConstantCompare(), which is compiled out under WOLFSSL_NO_CONST_CMP
  • [Info] New ML-DSA public-key derivation scratch not registered with WOLFSSL_CHECK_MEM_ZERO
  • [Info] WC_MLDSA_HAVE_CHECK_KEY and the fault-harden call site are gated on different macros; the second clause of WC_MLDSA_HAVE_CHECK_KEY is dead

Review generated by Skoll via Claude/Codex

Comment thread wolfcrypt/src/ecc.c Outdated
Comment thread wolfcrypt/src/asn.c Outdated
Comment thread tests/api/test_asn.c
Comment thread tests/suites.c
Comment thread wolfcrypt/src/asn.c
Comment thread tests/api/test_asn.c
Comment thread wolfcrypt/src/wc_mldsa.c Outdated
Comment thread doc/dox_comments/header_files/wc_mldsa.h
Comment thread wolfcrypt/src/wc_mldsa.c
Comment thread wolfcrypt/src/asn.c Outdated
@stenslae
stenslae force-pushed the fix-mldsa-privkey-derive-pubkey branch from e5044af to bc2c6f2 Compare August 6, 2026 20:03

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #10985

Scan targets checked: wolfcrypt-bugs, wolfcrypt-rs-bugs, wolfcrypt-src, wolfssl-bugs, wolfssl-src

Findings: 11
11 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment thread wolfcrypt/src/wc_mldsa.c
Comment thread wolfcrypt/src/asn.c
Comment thread tests/api/test_mldsa.c
Comment thread tests/api/test_mldsa.c
Comment thread tests/api/test_mldsa.c
Comment thread tests/api/test_asn.c
Comment thread src/ssl_api_pk.c
Comment thread tests/api/test_mldsa.c
Comment thread tests/api/test_asn.c
/* Fail each allocation in turn. Whatever fails, decode must never
* report a derived public key it does not have: the key comes back
* either fully derived and correct, or still ECC_PRIVATEKEY_ONLY. */
for (i = 1; EXPECT_SUCCESS() && (i <= totalAllocCount); i++) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] ECC OOM injection loop can pass vacuously · Weak or missing assertions

The loop is bounded by the counted totalAllocCount with no assertion that it is non-zero. In builds where the decode-plus-derive path allocates nothing in the armed window (e.g. ECC_CACHE_CURVE plus WOLFSSL_SP_NO_MALLOC, no WOLFSSL_SMALL_STACK), the body never executes and the test reports success without exercising any allocation-failure path.

Fix: Add ExpectIntGE(totalAllocCount, 1) before the loop, as mldsa_oom_derive_fail_level() does.

Comment thread tests/api/test_asn.c

@Frauschi Frauschi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐺 Skoll Code Review

Overall recommendation: REQUEST_CHANGES
Findings: 18 total — 8 posted, 10 skipped

Posted findings

  • [High] Three undisclosed sword32-overflow fixes in ML-DSA small-mem arithmetic ship with no executing testwolfcrypt/src/wc_mldsa.c:8773
  • [Medium] Under WOLF_CRYPTO_CB_FIND the devId gate in EccDerivePubBestEffort is still ineffective, so every SEC1 private-key decode fires an unsolicited EccMakePub callbackwolfcrypt/src/asn.c:33196
  • [Medium] Two sign-only ECC decode sites (pkcs7.c, ocsp.c) were not converted to derivePub=0wolfcrypt/src/pkcs7.c:2150
  • [Low] WC_MLDSA_HAVE_CHECK_KEY's second arm is unreachable and the condition is duplicated in two spellingswolfcrypt/src/wc_mldsa.c:262
  • [Low] Doxygen for PublicKeyToDer/KeyToDer states the derivation is unconditional, but the code gates it on output != NULLdoc/dox_comments/header_files/wc_mldsa.h:909
  • [Low] placeholder byte relies on an undocumented internal contract of SetAsymKeyDerwolfcrypt/src/wc_mldsa.c:14463
  • [Low] New key->ku scrub in ecc_make_pub_ex() is skipped on async WC_PENDING_E and never re-runwolfcrypt/src/ecc.c:5778-5791
  • [Info] Redundant guard terms in tests/suites.c and the new ML-DSA test guardstests/suites.c:1330
Skipped findings
  • [High] [BOT-DUPLICATE] Async derive failure leaves the key ECC_PRIVATEKEY with no public point, and the derivation runs unblinded
  • [Medium] ML-DSA export functions now mutate a caller-owned key from inside wc_MakeCert/wc_SignCert
  • [Medium] [BOT-DUPLICATE] mldsa_oom_derive_fail_level asserts an allocation count that is zero under WC_MLDSA_CACHE_PRIV_VECTORS
  • [Medium] Coverage gaps around the new derivation opt-outs and error paths
  • [Low] [BOT-DUPLICATE] Test comment claims a temporary RNG is used for blinding
  • [Low] [BOT-DUPLICATE] ECC OOM sub-test can still pass vacuously, and carries a dead guard condition
  • [Low] 23 added lines exceed the 80-column limit
  • [Low] [BOT-DUPLICATE] internal.c justification comment is wrong (also static ECDH, and WOLFSSL_CHECK_SIG_FAULTS)
  • [Info] PUBLIC_KEY_E replaces BAD_FUNC_ARG on four public verify entry points - needs a ChangeLog note
  • [Info] MakePublicKey encode scratch holding encoded t0 is not registered with wc_MemZero_Add

Review generated by Skoll via Claude/Codex

Comment thread wolfcrypt/src/wc_mldsa.c
#ifdef WOLFSSL_MLDSA_SMALL
/* Reduce before invntt to avoid sword32 overflow, as in
* mldsa_calc_t_std()'s vec_red() call. */
mldsa_poly_red(tt);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [High] Three undisclosed sword32-overflow fixes in ML-DSA small-mem arithmetic ship with no executing test
🚫 BLOCK test

Beyond the stated scope of the PR, the diff adds three new mldsa_poly_red() calls under WOLFSSL_MLDSA_SMALL, immediately before mldsa_invntt_full(): in mldsa_calc_t_small_mem (wc_mldsa.c:8773), in mldsa_sign_with_seed_mu (wc_mldsa.c:9926), and in the small-mem mldsa_verify_with_mu (wc_mldsa.c:11020). Each mirrors an existing mldsa_vec_red() in the corresponding standard path (wc_mldsa.c:8630, 9763, 10771) that the small-mem paths were missing. These are real latent correctness fixes: without the reduce, accumulated products can overflow sword32 before the inverse NTT, producing wrong keys and wrong signatures in WOLFSSL_MLDSA_SMALL + small-mem builds. The supporting guard widening at wc_mldsa.c:6298 and wc_mldsa.c:8289 (dropping !defined(WOLFSSL_MLDSA_VERIFY_SMALL_MEM)) exists solely to make mldsa_red/mldsa_poly_red compile for the new verify call site, confirming the additions are deliberate. Neither the PR description nor any of the four commit messages mentions them, no test fails without them, and no CI job would notice: --enable-mldsa=yes,small appears in exactly one entry (noasm-mldsa-align0, .github/workflows/wolfCrypt-Wconversion.yml:62) which sets no *_SMALL_MEM macro, and every ML-DSA small-mem entry (smallstack-noasm-small-mem, smallstack-intelasm-precalc, precalc-a-no-int128, verify-only-m32) is "check": false with --disable-crypttests, which .github/scripts/parallel-make-check.py:285 documents as skipping the make-check phase entirely. So WOLFSSL_MLDSA_SMALL combined with any small-mem variant is never built, and the small-mem paths that are built are never executed - this is exactly the code the PR rewrites wholesale into mldsa_calc_t_small_mem and then adds a second consumer for in wc_MlDsaKey_MakePublicKey.

Suggestion: Split the three mldsa_poly_red() additions into their own commit describing the overflow they fix, and add a CI entry that both builds and runs the combination:

{"name": "mldsa-small-small-mem", "minutes": 1,
"configure": ["--enable-cryptonly", "--enable-all-crypto", "--enable-mldsa=yes,small",
"CPPFLAGS=-DWOLFSSL_MLDSA_SIGN_SMALL_MEM -DWOLFSSL_MLDSA_VERIFY_SMALL_MEM -DWOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM"],
"check": true},

Recommendation: Separate the arithmetic fixes from the refactor so they are individually reviewable, and add at least one CI configuration combining WOLFSSL_MLDSA_SMALL with the small-mem macros that actually runs make check (the KATs in test_mldsa.c would catch a wrong t/w). Without a running config, both these fixes and the entire mldsa_calc_t_small_mem rewrite land unexercised.

@stenslae stenslae Aug 7, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did not reproduce any overflows, labeling this as a defensive change that follows convention. Added suggested CI config

Comment thread wolfcrypt/src/asn.c
if (key->type != ECC_PRIVATEKEY_ONLY) {
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [Medium] Under WOLF_CRYPTO_CB_FIND the devId gate in EccDerivePubBestEffort is still ineffective, so every SEC1 private-key decode fires an unsolicited EccMakePub callback
💡 SUGGEST bug

Sites: wolfcrypt/src/asn.c:33196; wolfcrypt/src/asn.c:33197-33202
The helper skips derivation only when key->devId != INVALID_DEVID, which is exactly backwards for WOLF_CRYPTO_CB_FIND builds: there a find callback owns keys that carry INVALID_DEVID, so those keys fall straight through to wc_ecc_make_pub_ex, and ecc_make_pub_ex deliberately drops the same devId gate under FIND (ecc.c:5722-5726) to issue wc_CryptoCb_EccMakePub(key, pub). The in-tree comment justifying that FIND behaviour (ecc.c:5706-5720) reasons explicitly about the signing path - "Reaching the software signer means the device already declined to sign (the whole-sign offload wc_CryptoCb_EccSign ran first)" - and concludes a find callback would likewise decline. That precondition does not exist during SEC1 key parsing, which is a brand-new call context this PR introduces for that callback. A find callback that answers EccMakePub from a resident device key rather than the supplied key->k therefore yields the wrong public point on decode, silently and with a 0 return. Separately, every other device-first branch in ecc.c wraps the devId test in #ifndef WOLF_CRYPTO_CB_FIND (ecc.c:4775, 5721, 5916, 7035, 9150, 10622, 11232, 11822), so the helper also breaks the file's own convention. The identical omission is repeated in the new ML-DSA entry point at wolfcrypt/src/wc_mldsa.c:11453, whose comment explicitly claims key->k may be a device handle - precisely the FIND case. This is the third review round in which the WOLF_CRYPTO_CB_FIND half of this issue has been raised; only the WOLF_CRYPTO_CB_ONLY_ECC exclusion landed.

Also, at wolfcrypt/src/asn.c:33197-33202 (Low, bugs scan) — devId early-return in EccDerivePubBestEffort does not cover WOLF_CRYPTO_CB_FIND:

The helper skips derivation for device-tagged keys by testing key->devId != INVALID_DEVID. But ecc_make_pub_ex() drops exactly that gate when WOLF_CRYPTO_CB_FIND is defined (ecc.c:5721-5725): #ifndef WOLF_CRYPTO_CB_FIND / if ((err == MP_OKAY) && (key->devId != INVALID_DEVID)) / #else / if (err == MP_OKAY) / #endif. So in a WOLF_CRYPTO_CB_FIND build, the keys the helper deliberately lets through (devId == INVALID_DEVID) are precisely the ones that then reach wc_CryptoCb_EccMakePub() anyway. This PR therefore introduces a crypto-callback invocation on every private-only SEC1 key decode in such builds — a call that did not previously happen at decode time at all. The hazard is already documented in the existing comment at ecc.c:5705-5720 ("if the device ignores key->k ... it returns dG instead of the requested kG"); this change widens that exposure from the ECDSA sign path to ordinary key loading. This point was raised in an earlier review round and the exclusion was applied to the test guard in test_asn.c but not to the library guard.

Suggestion:

Suggested change
#if defined(PLUTON_CRYPTO_ECC) || defined(WOLF_CRYPTO_CB)
/* Don't derive host-side public points for device-backed keys. Under
* WOLF_CRYPTO_CB_FIND any key may be device-backed - and ecc_make_pub_ex()
* drops its own devId gate there - so skip the derivation entirely. */
#ifndef WOLF_CRYPTO_CB_FIND
if (key->devId != INVALID_DEVID)
#endif
{
return;
}
#endif

Recommendation: Apply the #ifndef WOLF_CRYPTO_CB_FIND wrapper in both EccDerivePubBestEffort and wc_MlDsaKey_MakePublicKey. The early return in the helper is the only place this can be fixed - gating inside ecc_make_pub_ex would change the sign path too.

[wolfcrypt/src/asn.c:33197-33202] Skip the derivation outright under WOLF_CRYPTO_CB_FIND, since the devId gate cannot be relied on there:

#if defined(PLUTON_CRYPTO_ECC) || defined(WOLF_CRYPTO_CB)
/* Don't derive host-side public points for devId-tagged keys. Under
* WOLF_CRYPTO_CB_FIND, ecc_make_pub_ex() routes even INVALID_DEVID keys
* to a find callback, so skip the derivation entirely. */
#ifdef WOLF_CRYPTO_CB_FIND
return;
#else
if (key->devId != INVALID_DEVID) {
return;
}
#endif
#endif

Alternatively add !defined(WOLF_CRYPTO_CB_FIND) to the WOLFSSL_ECC_DERIVE_PUB_BEST_EFFORT guard at asn.c:33172-33179, mirroring the !defined(WOLF_CRYPTO_CB_ONLY_ECC) exclusion already present there.

Comment thread wolfcrypt/src/pkcs7.c
@@ -8670,7 +8670,10 @@ static int wc_PKCS7_KariParseRecipCert(WC_PKCS7_KARI* kari, const byte* cert,
else if (kari->direction == WC_PKCS7_DECODE) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [Medium] Two sign-only ECC decode sites (pkcs7.c, ocsp.c) were not converted to derivePub=0
💡 SUGGEST convention

The PR systematically converted probe-only and sign-only decode sites to EccPrivateKeyDecodeEx(..., 0) (src/internal.c:33097, src/sniffer.c:2843, src/ssl_api_hs.c:1269, src/ssl_api_pk.c:2379, src/ssl_load.c:575, wolfcrypt/src/asn.c:9620, asn.c:10270, evp_pk.c:201, pkcs7.c:8676) but missed two meeting the same criteria. wc_PKCS7_ImportECC (wolfcrypt/src/pkcs7.c:2150) decodes a key used only for signing, and the cost is doubled: the immediately-following wc_ecc_check_key() now sees ECC_PRIVATEKEY instead of ECC_PRIVATEKEY_ONLY and runs the full public-key validation (_ecc_validate_public_key, ecc.c:11395), adding a second scalar multiply for the order check on every wc_PKCS7_EncodeSignedData. src/ocsp.c:2455 decodes ca->key.ecc, consumed only by OcspResponseEncode at src/ocsp.c:2766 for signing. Note the PKCS#7 site does gain a genuine fix from the derivation (its wc_ecc_check_key previously failed with ECC_INF_E on a private-only key), so if it is left deriving that should be stated as the reason rather than left as an oversight.

Suggestion:

Suggested change
else if (kari->direction == WC_PKCS7_DECODE) {
/* Skip the best-effort public point derivation done on decode:
* this key is only used for signing, which needs the private
* scalar alone. */
ret = EccPrivateKeyDecodeEx(pkcs7->privateKey, &idx, privKey,
pkcs7->privateKeySz, 0);

Recommendation: Convert src/ocsp.c:2455 to EccPrivateKeyDecodeEx(..., 0), and either convert wolfcrypt/src/pkcs7.c:2150 or add a comment stating the derivation is kept deliberately so wc_ecc_check_key succeeds on private-only keys.

Comment thread wolfcrypt/src/wc_mldsa.c
* as an opt-in fault check under WC_MLDSA_FAULT_HARDEN. Small-mem re-derives
* streaming instead, so it never pulls CheckKey (and full matrix A) in.
* One macro each so the guards can't drift. */
#if defined(WOLFSSL_MLDSA_CHECK_KEY) || \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] WC_MLDSA_HAVE_CHECK_KEY's second arm is unreachable and the condition is duplicated in two spellings
💡 SUGGEST convention

Sites: wolfcrypt/src/wc_mldsa.c:262; wolfcrypt/src/wc_mldsa.c:258-268
WC_MLDSA_HAVE_CHECK_KEY is WOLFSSL_MLDSA_CHECK_KEY || (!NO_CHECK_KEY && WC_MLDSA_HAVE_MAKE_PUBLIC_KEY && WC_MLDSA_FAULT_HARDEN && !MAKE_KEY_SMALL_MEM). The second arm requires !WOLFSSL_MLDSA_NO_MAKE_KEY via WC_MLDSA_HAVE_MAKE_PUBLIC_KEY, and wolfssl/wolfcrypt/dilithium.h:317-323 derives WOLFSSL_MLDSA_PUBLIC_KEY and WOLFSSL_MLDSA_PRIVATE_KEY from exactly that, then auto-defines WOLFSSL_MLDSA_CHECK_KEY at dilithium.h:325-329 whenever both are set and WOLFSSL_MLDSA_NO_CHECK_KEY is not. The second arm is therefore strictly subsumed by the first, and WC_MLDSA_HAVE_CHECK_KEY is always identical to WOLFSSL_MLDSA_CHECK_KEY. The same condition is hand-copied into wolfssl/wolfcrypt/wc_mldsa.h:806-810 in a different form (expanding WC_MLDSA_HAVE_MAKE_PUBLIC_KEY inline and dropping !WOLFSSL_MLDSA_VERIFY_ONLY); the two are equivalent today only because VERIFY_ONLY force-defines NO_MAKE_KEY at dilithium.h:310-312 - precisely the fragile coupling the new macro was meant to remove. The PR comment One macro each so the guards can't drift is not achieved here.

Also, at wolfcrypt/src/wc_mldsa.c:258-268 (Info, bugs scan) — WC_MLDSA_HAVE_CHECK_KEY second arm is unreachable; small-mem still compiles CheckKey contrary to its comment:

The new WC_MLDSA_HAVE_CHECK_KEY macro is introduced with the stated goal that "Small-mem re-derives streaming instead, so it never pulls CheckKey (and full matrix A) in." That goal is not achieved, because the second arm of the macro can never be independently true. wolfssl/wolfcrypt/dilithium.h:317-330 already auto-defines WOLFSSL_MLDSA_CHECK_KEY whenever PUBLIC_KEY && PRIVATE_KEY && !WOLFSSL_MLDSA_NO_CHECK_KEY, and !WOLFSSL_MLDSA_NO_MAKE_KEY (required by WC_MLDSA_HAVE_MAKE_PUBLIC_KEY) implies both. So arm B implies arm A, WC_MLDSA_HAVE_CHECK_KEY is identically equal to WOLFSSL_MLDSA_CHECK_KEY, and every guard substitution in this PR is a semantic no-op. A WOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM build still compiles wc_MlDsaKey_CheckKey() along with its full matrix-A allocation (up to ~57 KB for ML-DSA-87), which is the exact footprint the macro was added to avoid. The same condition is hand-copied into wolfssl/wolfcrypt/wc_mldsa.h:806-812 (headers cannot see the .c-only macro), creating two copies that must be kept in sync for no benefit. No runtime misbehavior — the small-mem streaming re-derivation is still what executes — but the code-size promise in the comment is not delivered.

Suggestion:

Suggested change
#if defined(WOLFSSL_MLDSA_CHECK_KEY) || \
#ifdef WOLFSSL_MLDSA_CHECK_KEY
#define WC_MLDSA_HAVE_CHECK_KEY
#endif
/* and revert wc_mldsa.h:806-810 to the plain `#ifdef WOLFSSL_MLDSA_CHECK_KEY`
* it had before. */

Recommendation: Drop the dead second arm and revert the wc_mldsa.h prototype guard, so there is one condition rather than three spellings of the same thing.

[wolfcrypt/src/wc_mldsa.c:258-268] Either drop the redundant macro and keep using WOLFSSL_MLDSA_CHECK_KEY directly (removing the duplicated condition from wc_mldsa.h:806-812), or — if the small-mem size saving is actually wanted — make the exclusion real by having dilithium.h stop auto-defining WOLFSSL_MLDSA_CHECK_KEY under WOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM, so that:

#define WC_MLDSA_HAVE_CHECK_KEY

can genuinely be false in a small-mem build. Until one of those happens, correct the comment so it does not claim a code-size reduction that is not delivered.


Pass NULL as output to query the required buffer size.

If only the private key is set, the public key is derived and cached

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] Doxygen for PublicKeyToDer/KeyToDer states the derivation is unconditional, but the code gates it on output != NULL
🔧 NIT style

The added blocks for wc_MlDsaKey_PublicKeyToDer (doc line 909) and wc_MlDsaKey_KeyToDer (doc line 941) say "If only the private key is set, the public key is derived and cached in key" and add MEMORY_E/PUBLIC_KEY_E returns, with no mention that the size query is exempt. The implementation deliberately skips the derive when output == NULL (wc_mldsa.c:14398 and wc_mldsa.c:14513), precisely so a size query stays cheap and non-mutating, and neither error can be returned in that case. The equivalent block for wc_MlDsaKey_ExportPubRaw (doc line 787) is accurate because that function has no size-query mode.

Suggestion: If only the private key is set and output is non-NULL, the public key is
derived and cached in key; don't share key across threads during such a
call. A size query (output == NULL) never derives and never mutates key.

Recommendation: Qualify both blocks with the output != NULL condition so the documented error set matches the size-query path.

Comment thread wolfcrypt/src/wc_mldsa.c
byte placeholder = 0;
const byte* pub = (key->p != NULL) ? key->p : &placeholder;
#else
const byte* pub = key->p;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] placeholder byte relies on an undocumented internal contract of SetAsymKeyDer
🔧 NIT style

Both wc_MlDsaKey_PublicKeyToDer (wc_mldsa.c:14463) and wc_MlDsaKey_KeyToDer (wc_mldsa.c:14522) pass a one-byte stack object as pubKey alongside a pubKeyLen of up to 2592, relying on the callee not reading it. I verified this holds: SetAsymKeyDerPublic (wolfcrypt/src/asn.c:13389) only reaches its XMEMCPY(output, pubKey, pubKeyLen) inside if ((ret == 0) && (output != NULL)), and SetAsymKeyDer (wolfcrypt/src/asn.c:34327) only touches pubKey inside if (ret == 0 && output != NULL) in both the template and non-template branches. But neither function's header comment states that contract, so a future change reading pubKey during the sizing pass would become a 2591-byte stack over-read with no compiler or test signal.

Suggestion: Add to the doxygen of SetAsymKeyDer() and SetAsymKeyDerPublic() in wolfcrypt/src/asn.c:

  • @note pubKey must be non-NULL but is only dereferenced when output is
  •   non-NULL; callers performing a size query may pass a placeholder.
    

Recommendation: Document the "pubKey is not read when output == NULL" contract at both callees so the placeholder trick is protected by more than a comment at the call site.

Comment thread wolfcrypt/src/ecc.c
key->type = ECC_PRIVATEKEY;
}

#ifdef WOLFSSL_ECC_BLIND_K

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] New key->ku scrub in ecc_make_pub_ex() is skipped on async WC_PENDING_E and never re-run
🔧 NIT

This PR adds a scrub of key->ku - the unblinded private scalar that ecc_get_k() reconstructs from k ^ kb under WOLFSSL_ECC_BLIND_K - at the end of ecc_make_pub_ex(). The scrub is explicitly skipped when the operation returns WC_PENDING_E, and nothing re-runs it: the PR's own comment two blocks above states the async result lands directly in key->pubkey and "the operation is not re-issued", and mp_forcezero(key->ku) appears nowhere else in ecc.c except wc_ecc_free() (line 8592). So on the async path the unblinded scalar stays resident in the key struct for the whole key lifetime, which is exactly what WOLFSSL_ECC_BLIND_K exists to prevent. The exposure is slightly wider than the sp_int digits: ecc_make_pub_sw() at line 5560 calls wc_mp_to_bigint_sz(ecc_get_k(key), &ecc_get_k(key)->raw, keySz) before IntelQaEccPointMul(), copying the plain scalar into the ku->raw heap bigint for the hardware; sp_forcezero() clears a->raw via wc_bigint_zero() (sp_int.c:5395), so the skipped scrub also skips that copy. Reachable with WOLFSSL_ASYNC_CRYPT + WC_ASYNC_ENABLE_ECC_KEYGEN + HAVE_INTEL_QA + WOLFSSL_ECC_BLIND_K, and this PR newly routes every default wc_EccPrivateKeyDecode() through ecc_make_pub_ex(), so the path is hit once per key decode rather than only at keygen. Rated Low: exploitation requires local memory-forensics access, and other ECC operations already leave ku populated on their own (pre-existing), so the marginal exposure over the status quo is small - but the newly added mitigation is incomplete on a reachable path.

Recommendation: Scrub key->ku on the async-completion path as well, so the pending case is covered rather than permanently skipped. Either call mp_forcezero(key->ku) from the async resume/completion handler that harvests key->pubkey, or - since ecc_get_k() recomputes ku from k ^ kb on every call and the QAT operation reads ku->raw, not ku->dp - clear ku->dp immediately after wc_mp_to_bigint_sz() and clear ku->raw in the completion path. At minimum, document in the comment that the async path defers the scrub to wc_ecc_free() so the gap is a known, deliberate limitation.

Comment thread tests/suites.c
@@ -1329,8 +1329,11 @@ int SuiteTest(int argc, char** argv)
#if defined(WOLFSSL_HAVE_SLHDSA) && \
!defined(WOLFSSL_MLDSA_VERIFY_ONLY) && defined(WOLFSSL_HAVE_MLDSA) && \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ [Info] Redundant guard terms in tests/suites.c and the new ML-DSA test guards
🔧 NIT style

The new suites.c guard rationale checks out against tests/test-tls13-slhdsa-shake.conf and tests/test-tls13-slhdsa-sha2.conf: both sides do use ML-DSA-44 entity certs, so sign and verify are both needed, and the SLH-DSA root is only ever verified. WOLFSSL_MLDSA_NO_SIGN and WOLFSSL_MLDSA_NO_VERIFY are real macros. However, now that !defined(WOLFSSL_MLDSA_NO_SIGN) is present, the pre-existing !defined(WOLFSSL_MLDSA_VERIFY_ONLY) term is redundant - dilithium.h:310-312 force-defines WOLFSSL_MLDSA_NO_SIGN whenever VERIFY_ONLY is set. The same pattern appears in the new tests: test_mldsa_private_key_export_derives_public_key (tests/api/test_mldsa.c:7934) nests an inner !defined(WOLFSSL_MLDSA_NO_ASN1) && defined(WOLFSSL_MLDSA_PUBLIC_KEY) that merely restates its own outer guard, and several new guards spell out defined(WOLFSSL_MLDSA_PRIVATE_KEY) alongside !defined(WOLFSSL_MLDSA_NO_MAKE_KEY), which implies it via dilithium.h:321-323.

Suggestion: Drop !defined(WOLFSSL_MLDSA_VERIFY_ONLY) && from both suites.c guards, and drop the redundant inner #if at tests/api/test_mldsa.c:7934.

Recommendation: Trim the redundant terms so each guard states its condition once; this is the same drift risk as the WC_MLDSA_HAVE_CHECK_KEY finding.

@Frauschi

Frauschi commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

My new skoll findings are on top of the Fenrir ones (removed the duplicates).

@Frauschi Frauschi removed their assignment Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants